/*
 *  RL_socket.cpp
 *  RL_NetLib
 *
 *  Created by Andrew Butcher on 11/09/06.
 *  Copyright 2006 __MyCompanyName__. All rights reserved.
 *
 */

#include <stdexcept>
#include <arpa/inet.h>
#include "RL_socket.h"
#include <fcntl.h>


namespace prevRLGlue
{
/************
 * RL_socket 
 ************/
  
/* 
   Create our RL_socket
*/
RL_socket::RL_socket() : sys_socket(0)
{
}

RL_socket::RL_socket( os_socket sock ) : sys_socket( sock )
{
}


void RL_socket::setBlocking(bool f)
{
	long arg = fcntl(get_socket(), F_GETFL, NULL);

	if (f)
		arg &= ~O_NONBLOCK;
	else
		arg |= O_NONBLOCK;
	
	fcntl(get_socket(), F_SETFL, arg);
}


/* 
   Clean up our socket resources
*/
RL_socket::~RL_socket()
{
  this->close();
}

/* 
   Returns the underlying OS socket.
*/
os_socket RL_socket::get_socket()const
{
  return this->sys_socket;
}

/*
  Send msg across the socket, until all data is sent or there is an error.
  msg should be a buffer of at least "length" bytes long.
*/
int RL_socket::send_all( const char* msg, int length )
{
  int bytes_sent = 0;
  int msg_error = 0;
  
  while ( bytes_sent < length )
  {
    msg_error = ::send( this->get_socket(), msg + bytes_sent, length-bytes_sent, 0 );
    if ( msg_error == -1 ) { break; }
    else { bytes_sent += msg_error; } 
  }
  
  return bytes_sent;
}

/*
  Receive data from the socket, reading at most "length" data into msg.
  msg should be a buffer of at least "length" bytes.  Socket will 
  wait for "length" data to be recieved, or an error to occur before it returns.
*/
int RL_socket::recv_all( char* msg, int length )
{
  int bytes_recv = 0;
  int msg_error = 0;
  
  while ( bytes_recv < length )
  {
    msg_error = ::recv( this->get_socket(), msg + bytes_recv, length-bytes_recv, 0 );
    if  ( msg_error <= 0 ) { break; }
    else { bytes_recv += msg_error; }
  }
  
  return bytes_recv;
}

/* 
   Closes the connection
*/
int RL_socket::close()
{
  return ::close( this->get_socket() );
}

/*******************
 * RL_client_socket 
 *******************/	

RL_client_socket::RL_client_socket() : connected(false)
{
}

RL_client_socket::RL_client_socket(os_socket s, bool connected) : RL_socket(s)
{
  this->connected = connected;
}

RL_client_socket::~RL_client_socket()
{
}

bool RL_client_socket::connect_ip( const char* ip_address, short port )
{
  struct sockaddr_in dest_addr;
  
  this->sys_socket = socket(PF_INET, SOCK_STREAM, 0);
  dest_addr.sin_family = AF_INET;
  dest_addr.sin_port = htons(port);
  dest_addr.sin_addr.s_addr = inet_addr(ip_address);
  memset(&dest_addr.sin_zero, '\0', 8);
  
  this->connected = !connect(this->sys_socket, (struct sockaddr*)&dest_addr, sizeof(struct sockaddr));
  
  return this->is_connected();
}	

bool RL_client_socket::is_connected()
{
  return this->connected;
}

int RL_client_socket::recv_all( char* msg, int length )
{
  int result = RL_socket::recv_all(msg, length);
  if (result == 0) 
    this->connected = false;
  return result;
}

int RL_client_socket::close()
{
  this->connected = false;
  return RL_socket::close();
}

/******************
 * RL_server_socket 
 ******************/

RL_server_socket::RL_server_socket()
{
	connected = false; 
}

RL_server_socket::RL_server_socket(os_socket s) : RL_socket(s)
{
	connected = false;
}

RL_server_socket::~RL_server_socket()
{
}

void RL_server_socket::listen(short port)
{
  struct sockaddr_in server_address_info;
  int yes = 1;
  
  server_address_info.sin_family = AF_INET;
  server_address_info.sin_port = htons( port );
  server_address_info.sin_addr.s_addr = htonl(INADDR_ANY);
  memset(&server_address_info.sin_zero, '\0', 8);
  
  this->sys_socket = socket(PF_INET, SOCK_STREAM, 0);
  
  if ( this->sys_socket == -1 )
    throw std::runtime_error("Socket Failed");
  
  /* We don't really care if this fails... */
  setsockopt( this->sys_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int) );
  
  if ( ::bind( this->sys_socket, (struct sockaddr*)&server_address_info, sizeof(struct sockaddr) ) == -1 )
    throw std::runtime_error("Bind Failed");
  
  if ( ::listen( this->sys_socket, 50 ) == -1 )
    throw std::runtime_error("Listen Failed");
}

RL_socket RL_server_socket::accept()
{
  struct sockaddr_in client_address_info;
  unsigned int socket_size = sizeof(struct sockaddr_in);
  int accept_socket = ::accept( this->sys_socket, (struct sockaddr*)&client_address_info, &socket_size );
  
  if ( accept_socket == -1 ) 
  {
	connected = false; 
	throw std::runtime_error("Unable To Accept Connection");
  }else
	  connected = true; 

  
  return RL_socket(accept_socket);
}

bool RL_socket::isDataAvailable()
{
	

}



RL_socket_set::RL_socket_set()
{
  FD_ZERO(&this->fds_master);
  FD_ZERO(&this->fds_read);
}

RL_socket_set::~RL_socket_set()
{
}

int RL_socket_set::select()
{
  fds_read = fds_master;
  return ::select(this->fds_max+1, &this->fds_read, 0, 0, 0);
}

void RL_socket_set::add(RL_socket& client_connection)
{
  FD_SET(client_connection.get_socket(), &this->fds_master);
  
  if ( this->fds_max < client_connection.get_socket() )
    this->fds_max = client_connection.get_socket();
}

bool RL_socket_set::is_ready(RL_socket& client_connection)
{
  return FD_ISSET(client_connection.get_socket(), &this->fds_read);
}

}